

#ifndef __PLCAN_H__
#define __PLCAN_H__

#include "plconfig.h"


#define INVALID_CAN_CONFIG_INDEX (0xFFU)
#define INVALID_MAILBOX_INDEX (0xFFU)

#define CAN_GRP_USER_ALL (CAN_GRP_USER0 | CAN_GRP_USER1 | CAN_GRP_USER2 | CAN_GRP_USER3 | CAN_GRP_USER4 | CAN_GRP_USER5)
#define CAN_GRP_ALL (CAN_GRP_USER_ALL | CAN_GRP_DIAG | CAN_GRP_NW_MANAGER)


/* Enlarge CAN receive buff size, and manage it with a queue {{{ */
#define BUFF_SZ                                 (8U)

#define IS_BUFF_EMPTY(p_inf)                    (p_inf->front==p_inf->rear)

#define IS_BUFF_FULL(p_inf)                     (((p_inf->rear+1)%BUFF_SZ)==p_inf->front)

#define BUFF_ADD(p_inf,src,datalen)             p_inf->buffer[p_inf->rear].dlc=datalen;\
                                                memory_copy(src, &(p_inf->buffer[p_inf->rear].data[0]), datalen);\
                                                p_inf->rear=(p_inf->rear+1)%BUFF_SZ;\
                                                p_inf->used_buf_len=(p_inf->rear+BUFF_SZ-p_inf->front)%BUFF_SZ

/*Error dummy add overwriting old value without modify length and rear pointer */
#define BUFF_OVERADD(p_inf,src,datalen)         p_inf->buffer[p_inf->rear].dlc=datalen;\
                                                memory_copy(src, &(p_inf->buffer[p_inf->rear].data[0]), datalen)

#define BUFF_DEL(p_inf)                         /*memory_clear(&(p_inf->buffer[p_inf->front].data[0]), 8);*/\
                                                p_inf->front=(p_inf->front+1)%BUFF_SZ;\
                                                p_inf->used_buf_len=(p_inf->rear+BUFF_SZ-p_inf->front)%BUFF_SZ

/* Enlarge CAN receive buff size, and manage it with a queue }}} */


typedef enum {
	CAN_GRP_NONE = 0x00U,
	CAN_GRP_DIAG = 0x01U,
	CAN_GRP_NW_MANAGER = 0x02U,
	CAN_GRP_USER0 = 0x04U,
	CAN_GRP_USER1 = 0x08U,
	CAN_GRP_USER2 = 0x10U,
	CAN_GRP_USER3 = 0x20U,
	CAN_GRP_USER4 = 0x40U,
	CAN_GRP_USER5 = 0x80U,
} PlcanMessageGroup;

typedef struct {
	uint8 data[8U];
	uint8 dlc; /*data length of the buffer*/
	uint8 reserved[3];
} PlcanDataBuffer; /*size = 12*/

typedef struct {
    PlcanDataBuffer buffer[BUFF_SZ];	/* offset = 0 */

    uint8 front, rear,used_buf_len;		/* offset = 192 */

	bool enabled; /*enable or disable receiving of the coresponding canid*/ /* offset = 195 */
	uint8 config_index;                 /* offset = 196 */

	uint8 newest_buffer_index; /*The index of this double buffer that has the newest received data.*/  /* offset = 197 */
	uint8 rx_data_valid;               /* offset = 198 */
	uint8 rx_data_invalid;             /* offset = 199 */

	uint16 rx_consecutive_data_error_count;  /*TODO: check when to clear*/    /* offset = 200*/
	uint16 nomsg_cycle_count; /*The cycle count will increase when no message is received, and will be cleared when received a message*/   /* offset = 202*/
	uint16 rx_alive_count;                     /* offset = 204*/
	uint8 rx_timedout;                         /* offset = 206*/
	uint8 reserved;                            /* offset = 207*/
} PlcanRxInfo; /*size = 208*/

typedef struct {
	PlcanDataBuffer buffer;

	uint8 config_index;
	bool  enabled;

	bool  buffer_queued;
	bool  interrupt_suppressed;
} PlcanTxInfo;


typedef bool (*PlcanRxDataPreChecker)(PlcanDataBuffer *data_buffer);
typedef bool (*PlcanRxDataPreHandler)(PlcanDataBuffer *data_buffer, void *arguments);
typedef uint8 (*PlcanRxCallback)(PlcanDataBuffer *data_buffer, bool pre_handle_result);
typedef void (*PlcanRxTimeoutCallback)(uint8 config_index);

typedef bool (*PlcanTxFillHandler)(PlcanDataBuffer *buffer, void *arguments);
typedef void (*PlcanTxPostFiller)(PlcanDataBuffer *buffer);
typedef void (*PlcanTxCallback)(uint8 config_index, bool transmit_result);
typedef void (*PlcanTxBusyCallback)(uint8 config_index);



/*
 * Define CANID for receiving data.
 * */
typedef struct {
	uint32 canid;
	uint8 rx_can_group; 
    
	/*The function to check the checksum of alive count when necessary*/
	PlcanRxDataPreChecker pre_checker;
	/*The function handler to extract out signals before invoking the callback function*/
	PlcanRxDataPreHandler pre_handler;
	void *pre_handler_arguments; /*The arguments to pass to pre_handler*/
	PlcanRxCallback rx_callback;

	uint16 timeout_cycle; 
	PlcanRxTimeoutCallback timeout_callback;

	uint8* config_index_ref;
} PlcanRxidDef;


/*Define CANID for transmitting data.*/
typedef struct {
	uint32 canid;
    
	uint8 tx_can_group;
	uint8 dlc; /*Fill it when this canid has a fixed dlc, otherwise fill it to 0, and the dlc will be overwritten by PlcanTxInfo.buffer.dlc*/

    
    /*The period to transmit this message. unit: ms.
	 * Note: the value of the period must be a multiple of
	 * the period of the transmit task!!
	 * */
    
	uint16 transmit_period_ms;

	/*The time offset(ms) within the period to transmit this message*/
	uint16 transmit_offset_ms;

	PlcanTxFillHandler fill_handler;
	void *fill_handler_arguments;

	/*The function to fill checksum or alive count when necessary*/
	PlcanTxPostFiller post_filler;
	PlcanTxCallback tx_callback;

	uint8 *config_index_ref;
} PlcanTxidDef;



extern bool plcan_has_rx_ext_canid;

extern const uint8 plcan_rx_canid_count;
extern const PlcanRxidDef plcan_rx_defs[];
extern PlcanRxInfo plcan_rx_infos[];

extern const uint8 plcan_tx_canid_count;
extern const PlcanTxidDef plcan_tx_defs[];
extern PlcanTxInfo plcan_tx_infos[];

extern bool   plcan_is_busoff;
extern uint16 plcan_tx_success_count_since_last_buson;
extern uint16 plcan_busoff_count;
extern uint16 plcan_busoff_hold_time;
extern uint16 plcan_buson_hold_time;
extern bool plcan_busidle_filtered;
extern bool plcan_should_record_busoff_dtc;
extern uint16 plcan_elapsed_time_since_last_busoff_revocery;
extern uint16 plcan_elapsed_time_since_last_diag_request;


extern uint8  plcan_busoff_recovery_thresholdcnt;
extern uint16 plcan_busoff_recovery_timecnt;
extern bool plcan_enable_transmit_msg;
extern bool plcannm_disable_transmit;
extern bool plcannm_disbale_receive;


extern void plcan_config_rx_enable_at_startup(void);
extern void plcan_init_can(void);
extern void plcan_enter_sleep(void);

extern void plcan_error_handler(void);
extern void plcan_tx_busy_handler(void);

extern void plcan_receive_handler(uint32 can_id,uint8 dlc,const uint8 *buf);
extern void plcan_rxmsg_task(void);

extern void plcan_txmsg_task(void);
extern void plcan_transmit_complete_handler(uint32 can_transmit_last_id);
extern void plcan_transmit_byconfig(uint8 config_index, bool should_call_fill);

extern void plcan_busoff_diag_task(void);
extern void plcan_enable_tx_by_group(uint8 can_group, bool enabled);
extern void plcan_enable_rx_by_group(uint8 can_group, bool enabled);
extern void plcan_cancel_transmit(uint8 config_index);

extern void plcan_trxmsg_task(void);

#define plcan_enable_tx_canid(config_index) (plcan_tx_infos[(config_index)].enabled = TRUE)
#define plcan_disable_tx_canid(config_index) (plcan_tx_infos[(config_index)].enabled = FALSE)

#define plcan_enable_rx_canid(config_index) (plcan_rx_infos[(config_index)].enabled = TRUE)
#define plcan_disable_rx_canid(config_index) (plcan_rx_infos[(config_index)].enabled = FALSE)
#define plcan_rx_timedout(config_index) (plcan_rx_infos[(config_index)].rx_timedout == TRUE)
#define plcan_rx_data_valid(config_index) (plcan_rx_infos[(config_index)].rx_data_valid)
#define plcan_rx_data_invalid(config_index) (plcan_rx_infos[(config_index)].rx_data_invalid)
#define plcan_rx_consecutive_data_error_count(config_index) (plcan_rx_infos[(config_index)].rx_consecutive_data_error_count)
#define plcan_rx_alive_count(config_index) (plcan_rx_infos[(config_index)].rx_alive_count)

#define plcan_get_tx_buffer(config_index) ((PlcanDataBuffer*)(&(plcan_tx_infos[(config_index)].buffer)))

#endif /* __PLCAN_H__ */
